package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author liyang
 * @version 1.0
 * @package socket
 * @file Server.java
 * @description
 * @time 2024-04-02 21:38
 */
public class Server {
    private ServerSocket serverSocket;
    //4.14
    //4.15用collections.xxx令ArrayList成为一个并发安全的集合
    private List<PrintWriter> allOut
            = Collections.synchronizedList(new ArrayList<>());

    //构造方法
    public Server(){
        //实例化serverSocket
        try {
            System.out.println("正在启动服务端");
            serverSocket = new ServerSocket(8088);
            System.out.println("完毕！");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //Start方法：让服务端开始工作的方法
    public void start(){
        try {
            while (true) {
                System.out.println("等待客户端...");
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了");

                //解socket错误
                ClientHandler handler = new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) {
        //实例化一个server方法,跑起来
        Server server = new Server();
        server.start();

    }

    private class  ClientHandler implements Runnable{
        //socket报错解法，把参数给一个新建的构造器，在构造器中，再把参数赋给一个声明好的属性
        private Socket socket;

        private String host;

        public ClientHandler(Socket socket) {
            this.socket = socket;
            host = socket.getInetAddress().getHostAddress();
        }


        @Override
        public void run() {

                PrintWriter pw = null;
            //以下内容为ctrl+s(剪切)原来start方法里的内容
            //粘贴以后ctrl+alt+t ➡ try,catch,finally处理错误

            try {
                //复制PWDemo2内容
                InputStream in =  socket.getInputStream();
                InputStreamReader isr =new InputStreamReader(in);
                BufferedReader br = new BufferedReader(isr);

//            java.net.SocketException: Connection reset
//            原因:客户端没有进行挥手操作就断开了连接。
                //使服务端能回信息4.14新进度）
                OutputStream out = socket.getOutputStream();
                OutputStreamWriter osw = new OutputStreamWriter(out,StandardCharsets.UTF_8);
                BufferedWriter bw = new BufferedWriter(osw);
                pw = new PrintWriter(bw,true);
                //并发安全，样例看图
                //已经变为并发安全的数组，就不需要上锁了
                allOut.add(pw);

                //把对所有客户端遍历说话的动作集合成一个方法来调用
                sendMessage(host+"上线了，当前在线人数"+allOut.size());

                String message;
                while((message = br.readLine()) != null) {

                    sendMessage(host+"说"+message);
                }


            } catch (IOException e) {

            } finally {
                //客户端断开：从数组中删掉它的流
                //同add同理
                allOut.remove(pw);

                sendMessage(host+"下线了，当前在线人数"+allOut.size());
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        //专门用来对所有客户端广播的方法
        private void sendMessage (String line){
            System.out.println(line);
            //foreach:集合自身的方法，能实现互斥，迭代器就不行
            allOut.forEach(p->p.println(line));
            //遍历的时候不能做增删（修改集合）
//            synchronized (allOut) {
//                for (PrintWriter p : allOut) {
//                    p.println(line);
//                }
//            }


        }

    }

}
